/**
 * 上海中赢金融信息服务有限公司
 * Copyright (c) 2017-2027 Chinazyjr,Inc.All Rights Reserved.
 */

package com.sys.midware.mongo.core;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Vector;

import org.apache.commons.beanutils.BeanUtils;
import org.bson.types.ObjectId;

import com.google.gson.Gson;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.util.JSON;
import com.sys.midware.logger.Logger;
import com.sys.midware.logger.LoggerFactory;

/**
 * <b>ClassName:</b> MongoUtil.java <br/>
 * <b>Description:</b> TODO <br/>
 * <b>Date:</b> 2016年1月8日 下午2:51:35<br/>
 * 
 * @author mobing
 * @version
 */
public class MongoUtil {
    private static Logger logger = LoggerFactory.getLogger(MongoUtil.class);

    /**
     * dualDBObj4Map:处理数据库对象，将数据库对象处理成Map
     * 主要是数据库字段为dbolist，objectId和baseObject等数据类型不统一
     * 
     * @author mobing
     */
    @SuppressWarnings("unchecked")
    public static Map<String, Object> dualDBObj4Map(DBObject dbo) {
        Map<String, Object> dbomap = dbo.toMap();
        Map<String, Object> retMap = new HashMap<String, Object>();
        for (Entry<String, Object> map : dbomap.entrySet()) {
            Object valObj = map.getValue();
            if (valObj instanceof ObjectId) { // type of ObjectId
                ObjectId _id = (ObjectId) valObj;
                retMap.put(map.getKey(), _id.toString());
            } else if (valObj instanceof BasicDBList) {// type of BasicDBList
                BasicDBList dboList = (BasicDBList) map.getValue();
                StringBuffer sb = new StringBuffer();
                for (Object obj : dboList) {
                    String listDBO = (String) obj;
                    sb.append(listDBO == null ? "" : listDBO);
                    sb.append(",");
                }
                String re = sb.toString();
                if (re != null && re.endsWith(",")) {
                    re = sb.substring(0, sb.length() - 1);
                }
                retMap.put(map.getKey(), re);
            } else if (valObj instanceof BasicDBObject) {// type of
                                                         // BasicDBObject
                BasicDBObject bdbo = (BasicDBObject) map.getValue();
                retMap.put(map.getKey(), (bdbo == null ? null : bdbo.toString()));
            }else {
                retMap.put(map.getKey(), valObj);
            }
        }
        retMap.remove("_id");
        return retMap;
    }

    /**
     * 将Java对象转换成DBObject
     * 
     * @param
     * @return DBObject
     */
    @SuppressWarnings("unchecked")
    public static DBObject convertToDBObject(Object obj) {

        if (obj == null) {
            throw new IllegalArgumentException("arugument obj is not null");
        }
        DBObject dbo = null;
        if (obj instanceof Map<?, ?>) {// Map
            dbo = MongoUtil.map2DBObject((Map<String, Object>) obj);
        } else if (obj instanceof DBObject) {// DBObject
            dbo = (DBObject) obj;
        } else {// Java Bean
            dbo =    bean2DBObject(obj);//支持对象嵌套
            //dbo = bean2DBObjectWithFirstUpperCase(obj);
        }
        return dbo;
    }

    /**
     * 将JavaBean转成DBObject
     * 支持对象嵌套
     * @param obj
     * @return
     */
    private static DBObject bean2DBObject(Object bean) {
        Gson gson = new Gson();
        String json = gson.toJson(bean);
        return (DBObject) JSON.parse(json);
    }

    private static <T> DBObject bean2DBObject(Class<T> clazz) {
         Gson gson = new Gson();
         String json = gson.toJson(clazz);
        return (DBObject) JSON.parse(json);
    }

    /**
     * 将map转成DBObject
     * 
     * @param map
     * @return
     */
    public static DBObject map2DBObject(Map<String, Object> map) {
        DBObject obj = new BasicDBObject();
        obj.putAll(map);
        BasicDBObject doc = new BasicDBObject();
        for (Map.Entry<String, Object> entity : map.entrySet()) {
            doc.append(entity.getKey(), entity.getValue());
        }
        return obj;
    }

    /**
     * 把DBObject转换成bean对象
     * 
     * @param dbObject
     * @param bean
     * @return
     */
    private static <T> T DBObject2Bean(DBObject dbObject, T bean) {
        try {
            if (dbObject == null) {
                return null;
            }
            if (bean == null) {
                return null;
            }
            Field[] fields = bean.getClass().getDeclaredFields();
            for (Field field : fields) {
                String varName = field.getName();
                Object object = dbObject.get(varName);
                if (object != null) {
                    BeanUtils.setProperty(bean, varName, object);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bean;
    }

    /**
     * JavaBean转Map
     * 
     * @param
     * @return Object
     */
    public static Object map2Bean(Map<String, Object> map, Object obj) {
        if (map == null || obj == null) {
            return null;
        }
        try {
            BeanUtils.populate(obj, map);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return obj;
    }

    /**
     * JavaBean转成Map<String,Object>
     * 
     * @param
     * @return Map<String,Object>
     */
    public static Map<String, Object> bean2Map(Object obj) {

        if (obj == null) {
            return null;
        }
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();
                // 过滤class属性
                if (!key.equals("class")) {
                    // 得到property对应的getter方法
                    Method getter = property.getReadMethod();
                    Object value = getter.invoke(obj);
                    if(value==null){
                        continue;
                    }
                    map.put(key, value);
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        return map;
    }

    @SuppressWarnings("unchecked")
    public static <E , V extends Object> Map<E, V> DBObject2Map(DBObject dbObj) throws Exception {
        if (dbObj == null)
            return null;

        return dbObj.toMap();
    }

    public static <T> List<T> cursor2List(DBCursor cursor, Class<T> clazz) throws Exception {
        List<T> list = new ArrayList<T>();
        while (cursor.hasNext()) {
            DBObject dbObj = cursor.next();
            T t = DBObject2Bean(dbObj, clazz);
            list.add(t);
        }
        return list;
    }

    public static <T> T DBObject2Bean(DBObject dbObj, Class<T> clazz) throws Exception {
        if (dbObj == null)
            return null;
        T t = clazz.newInstance();
        recyleSetter(dbObj, t);
        return t;
    }

    /***
     * 递归所有属性
     * 
     * @param dbObj
     * @param bean
     * @throws Exception
     */
    private static void recyleSetter(DBObject dbObj, Object bean) throws Exception {
        Iterator<String> it = dbObj.keySet().iterator();
        while (it.hasNext()) {
            try {
                String key = it.next();
                Object value = dbObj.get(key);
                recyleValueJutisy(key, value, bean);
            } catch (Exception e) {
                logger.error("recyleSetter fail", e);
            }
        }
    }

    @SuppressWarnings("rawtypes")
    private static void recyleValueJutisy(String key, Object value, Object bean) throws Exception {
        if (value instanceof BasicDBList) {
            BasicDBList dblist = (BasicDBList) value;
            Iterator<Object> it = dblist.iterator();
            Class<?> type = null;
            try {
                type = getPropertyType(bean, key);
                List<Object> list = new ArrayList<Object>();
                while (it.hasNext()) {
                    Object object = it.next();
                    if (object instanceof DBObject) {
                        DBObject dbItem = (DBObject) object;
                        Object o = type.newInstance();
                        recyleSetter(dbItem, o);
                        list.add(o);
                    } else if (object instanceof String) {
                        list.add(object);
                    }
                }
                BeanUtils.setProperty(bean, key, list);
            } catch (NoSuchFieldException e) {
            }

        } else if (value instanceof DBObject) {
            DBObject dbItem = (DBObject) value;
            Class<?> type = getPropertyType(bean, key);
            Class tclazz = Timestamp.class;
            if (type == tclazz) {
                // 时间类型
                Object otime = dbItem.get("time");
                if (otime != null) {
                    long time = Long.parseLong(String.valueOf(otime));
                    Timestamp st = new Timestamp(time);
                    BeanUtils.setProperty(bean, key, st);
                }
            } else {
                Object o = type.newInstance();
                recyleSetter(dbItem, o);
                BeanUtils.setProperty(bean, key, o);
            }
        } else {
            Class<?> clazz = bean.getClass();
            Field field;
            try {
                field = clazz.getDeclaredField(key);
                if (field != null) {
                    if (value != null) {
                        if ("".equals(value)) {
                            if (field.getType() == String.class)
                                BeanUtils.setProperty(bean, key, value);
                        } else {
                            BeanUtils.setProperty(bean, key, value);
                        }
                    }

                }
            } catch (NoSuchFieldException e) {

            }

        }
    }

    /**
     * @param bean
     * @param key
     * @return
     * @throws Exception
     */
    private static Class<?> getPropertyType(Object bean, String key) throws Exception {
        Class<?> clazz = bean.getClass();
        Field f = clazz.getDeclaredField(key);
        Class<?> t = f.getType();
        if (t.getName().startsWith("java.lang")) {
            return t;
        }
        if (t.isAssignableFrom(List.class) || t.isAssignableFrom(Set.class) || t.isAssignableFrom(Vector.class)) {
            Type gt = f.getGenericType();
            if (gt == null) {
                return t;
            }
            if (gt instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType) gt;
                Class<?> genericClazz = (Class<?>) pt.getActualTypeArguments()[0];
                if (genericClazz != null) {
                    return genericClazz;
                }
            }
        }
        return t;
    }

    /**
     * 把实体bean对象转换成DBObject
     * 
     * @param bean
     * @return
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    @SuppressWarnings("unchecked")
    public static <T> DBObject bean2DBObjectWithFirstUpperCase(T bean) {
        if (bean == null) {
            return null;
        }
        DBObject dbObject = new BasicDBObject();
        try {

            // 获取对象对应类中的所有属性域
            Field[] fields = bean.getClass().getDeclaredFields();
            for (Field field : fields) {
                // 获取属性名
                String varName = field.getName();
                //保持第一个字母大写
//                varName = StringUtil.toFirstUpperCase(varName);
                // 修改访问控制权限
                boolean accessFlag = field.isAccessible();
                if (!accessFlag) {
                    field.setAccessible(true);
                }
                // 静态和final类型不需要注入
                int modifier = field.getModifiers();
                if (Modifier.isStatic(modifier) || Modifier.isFinal(modifier)) {
                    continue;
                }
                Object param = field.get(bean);
                if (param == null) {
                    continue;
                } else if (param instanceof Integer) {// 判断变量的类型
                    int value = ((Integer) param).intValue();
                    dbObject.put(varName, value);
                } else if (param instanceof String) {
                    String value = (String) param;
                    dbObject.put(varName, value);
                } else if (param instanceof Double) {
                    double value = ((Double) param).doubleValue();
                    dbObject.put(varName, value);
                } else if (param instanceof Float) {
                    float value = ((Float) param).floatValue();
                    dbObject.put(varName, value);
                } else if (param instanceof Long) {
                    long value = ((Long) param).longValue();
                    dbObject.put(varName, value);
                } else if (param instanceof Boolean) {
                    boolean value = ((Boolean) param).booleanValue();
                    dbObject.put(varName, value);
                } else if (param instanceof Date) {
                    Date value = (Date) param;
                    dbObject.put(varName, value);
                }else if (param instanceof List) {  
                    List<Object> list = (List<Object>) param;  
                    dbObject.put(varName, list);  
                } else if (param instanceof Map) {  
                    Map<Object, Object> map = (Map<Object, Object>) param;  
                    dbObject.put(varName, map);  
                }  
                // 恢复访问控制权限
                field.setAccessible(accessFlag);
            }
            return dbObject;
        } catch (Exception e) {

            logger.error("", e);
        }
        return dbObject;
    }
}
